﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Galoap
{
    partial class User
    {
        /// <summary>
        /// Registers a new user in the db
        /// </summary>
        /// <param name="user"></param>
        public static void Register(User user)
        {
            int nick = GaloapDatabase.Database.User.Count(u => u.NickName.Equals(user.NickName));
            int email = GaloapDatabase.Database.User.Count(u => u.Email.Equals(user.Email));
            if (nick == 0 && email == 0)
            {
                GaloapDatabase.Database.AddToUser(user);
                GaloapDatabase.Database.SaveChanges();
            }
            else
            {
                throw new System.ArgumentException("User with this " + (nick != 0 ? "nickname" : "email") + " already exists.");
            }
        }

        /// <summary>
        /// User Login
        /// </summary>
        /// <param name="nickname"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public static User Login(string nickname, string password)
        {
            User user = GetUserByNickname(nickname);
            if (user.Password.Equals(password))
            {
                return user;
            }
            else throw new System.ArgumentException("Password is incorrect.");
        }
        
        /// <summary>
        /// Returns the list of all users
        /// </summary>
        /// <returns></returns>
        public static List<User> GetAllUsers()
        {
            return GaloapDatabase.Database.User.ToList();
        }
        
        /// <summary>
        /// Given a userId returns user data
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public static User GetUserByID(int userID)
        {
            try
            {
                return GaloapDatabase.Database.User.First(u => u.UserID == userID);
            }
            catch (Exception) 
            { 
                throw new System.ArgumentException(userID + " :User ID not found."); 
            }
        }
        
        /// <summary>
        /// Given a nickname this method returns the user data
        /// </summary>
        /// <param name="nickname"></param>
        /// <returns></returns>
        public static User GetUserByNickname(string nickname)
        {
            try
            {
                return GaloapDatabase.Database.User.First(u => u.NickName.Equals(nickname));
            }
            catch (Exception) 
            { 
                throw new System.ArgumentException(nickname + " :Nickname not found.");
            }
        }
        
        /// <summary>(Massimiliano)
        /// Given a email this metod retunrn the email data
        /// </summary>
        /// <param name="email"></param>
        /// <returns></returns>
        public static User GetUserByEmail(string email)  {
            try  {
                return GaloapDatabase.Database.User.First(u => u.Email.Equals(email));
            }
            catch (Exception)   {
                throw new System.ArgumentException(email + " :Email not found.");
            }
        }
        
        /// <summary>
        /// Given an user this method returns the number of games he has ever played
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static int GetGamesPlayedCount(User user)
        {
            return Game.GetAllUserGames(user).Count();
        }
        
        /// <summary>
        /// Returns the number of time this game has been played
        /// </summary>
        /// <returns></returns>
        public int GetGamesPlayedCount()
        {
            return Game.GetAllUserGames(this).Count();
        }
        
        /// <summary>
        /// Given an user this method returns the game that he has been played the most
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public static Game GetMostPlayedGame(User user)
        {
            List<Game> games = Game.GetAllUserGames(user);
            return (from game in games
                    group game by game.GameID into gameGroup
                    orderby gameGroup.Count() descending
                    select gameGroup).First().First();
        }

        /// <summary>
        /// Given a game returns the user with highest ranking
        /// </summary>
        /// <param name="game"></param>
        /// <returns></returns>
        public static User GetUserWithHighestRanking(Game game)
        {
            game.UserGame.Load();
            UserGame ug = (from usergame in game.UserGame
                    orderby usergame.ranking descending
                    select usergame).First();

            ug.UserReference.Load();
            return ug.User;
        }

        /// <summary>
        /// Massimiliano
        /// </summary>
        /// <param name="game"></param>
        /// <returns></returns>
        public static List<UserGame> GetRanking(Game game) {
            List<User> u = new List<User>();
            game.UserGame.Load();
            return (from usergame in game.UserGame
                           orderby usergame.ranking descending
                           select usergame).ToList();
        }

        /// <summary>
        /// Given a game returns the user with lowest ranking
        /// </summary>
        /// <param name="game"></param>
        /// <returns></returns>
        public static User GetUserWithLowestRanking(Game game)
        {
            game.UserGame.Load();
            UserGame ug = (from usergame in game.UserGame
                    orderby usergame.ranking ascending
                    select usergame).First();
            
            ug.UserReference.Load();
            return ug.User;
        }

        /// <summary>
        /// returns the game that has been played more
        /// </summary>
        /// <returns></returns>
        public Game GetMostPlayedGame()
        {
            return GetMostPlayedGame(this);
        }
        
        /// <summary>
        /// Given an user this method returns last GameSession
        /// </summary>
        /// <returns></returns>
        public GameSession GetLastGameSession()
        {
            this.GameSession.Load();
            return (from GameSession in this.GameSession
                    where GameSession.User.UserID == this.UserID
                    orderby GameSession.Date descending
                    select GameSession).First();
        }

        /// <summary>
        /// Returns the k last GameSessions
        /// </summary>
        /// <param name="k"></param>
        /// <returns></returns>
        public List<GameSession> GetLastKGameSessions(int k)
        {
            this.GameSession.Load();
            return (from GameSession in this.GameSession
                    orderby GameSession.Date descending
                    select GameSession).Take(k).ToList();
        }
        
        /// <summary>
        /// Returns the last game 
        /// </summary>
        /// <returns></returns>
        public Game GetLastGame()
        {
            this.GameSession.Load();
            return GetLastGameSession().Game;
        }
        
        /// <summary>
        /// Returns the last score 
        /// </summary>
        /// <returns></returns>
        public int GetLastScore()
        {
            this.GameSession.Load();
            return GetLastGameSession().Score;
        }
        
        /// <summary>
        /// Returns the last level
        /// </summary>
        /// <returns></returns>
        public int GetLastLevel()
        {
            this.GameSession.Load();
            return GetLastGameSession().Level;
        }
        
        /// <summary>
        /// Returns the number of GameSessions
        /// </summary>
        /// <returns></returns>
        public int GetGameSessionsCount()
        {
            this.GameSession.Load();
            return GameSession.Count;
        }
        
        /// <summary>
        /// Given a game this method gets the ranking for it
        /// </summary>
        /// <param name="game"></param>
        /// <returns></returns>
        public double GetRankingForGame(Game game)
        {
            game.UserGame.Load();
            // game.UserGame.First();
            return (from userGame in game.UserGame // Galoap.GaloapDatabase.Database.UserGame
                    where userGame.User.UserID == this.UserID // userGame.Game.GameID == game.GameID
                    select userGame).First().ranking;
        }

        
        /// <summary>
        /// Returns the game that has been played most frequently
        /// </summary>
        /// <returns></returns>
        public Game GetMostFrequentlyPlayedGame()
        {
            this.GameSession.Load();
            foreach (GameSession gamesession in this.GameSession) gamesession.GameReference.Load();
            return (from GameSession in this.GameSession
                    group GameSession by GameSession.Game.GameID into GameSessionGroup
                    orderby GameSessionGroup.Count() descending
                    select GameSessionGroup).First().First().Game;
        }

        /// <summary>
        /// Returns the total score by this user for a certain game
        /// </summary>
        /// <returns></returns>
        public int GetTotalScoreForGame(Game game)
        {
            int score = 0;
            this.GameSession.Load();
            foreach (GameSession gamesession in this.GameSession) gamesession.GameReference.Load();
            List<GameSession> sessions = (from gamesession in this.GameSession
                                          where gamesession.Game.GameID == game.GameID
                                          select gamesession).ToList();
            foreach (GameSession gs in sessions)
            {
                score += gs.Score;
            }
            return score;
        }

        /// <summary>
        /// Returns a list of active user on a certain date
        /// </summary>
        /// <returns></returns>
        public static List<User> GetActiveUsersByDate(DateTime date)
        {
            return (from user in GaloapDatabase.Database.User
                    join GameSession in GaloapDatabase.Database.GameSession.Include("User") on user.UserID equals GameSession.User.UserID
                    where GameSession.Date.Year == date.Year && GameSession.Date.Month == date.Month && GameSession.Date.Day == date.Day
                    select user).Distinct().ToList();
        }
    }
}